//
//$Id: gridmin.h 687 2011-03-13 12:21:29Z Oleg.Bulychov $
#pragma once

#include "src/heo/include/random.h"
//#include "src/heo/include/calc_hash.h"
#include "src/heo/include/reflection.h"
#include "src/heo/include/solution.h"
#include "src/heo/include/optimization_direction.h"

#include "src/heo/include/transmit_matrix2d.h"
#include "src/heo/include/matrix2d.h"
#include "grids_common.h"

//class SubsetEx: public Subset
//{
//    public Subset_Serializer* serializer_;
//};
//===============

namespace meta
{

template<>
struct is_visitable< Subset >
{
    enum {result = 0};
};

}
template<class V>
struct transmit_handler<Subset, V>
{
    static void visit(Subset& s, V& v)
    {
        visit(s, v, meta::int2type<V::transmit_tag>());
    }

    static void visit(Subset& s, V& bcast, meta::int2type<BCAST_TAG>)
    {
        if (bcast.is_Root())
        {
            visit(s, bcast, meta::int2type<SEND_TAG>());
        }
        else
        {
            visit(s, bcast, meta::int2type<RECV_TAG>());
        }
    }

    static void visit(Subset& s, V& send, meta::int2type<SEND_TAG>)
    {
        Subset_Serializer ss;//???????
        unsigned int len;
        byte_t* temp = s.serialize(ss, len);
        size_t dim = len;
        send << dim;
        send.visit_Array(temp, dim, "", 0);
        delete[] temp;
    }

    static void visit(Subset& s, V& recv, meta::int2type<RECV_TAG>)
    {
        size_t dim = 0;
        recv >> dim;
        byte_t* temp = new byte_t[dim];
        recv.visit_Array(temp, dim, "", 0);
        s.deserialize(temp);
        delete[] temp;
    }
};
//===============

struct GridMin_Config
{
    static char const* get_Name() { return "gridmin"; }
    static char const* get_Section() { return get_Name(); }

    template<class V>
    void accept(V& v)
    {
    }
};

struct GridMin_Problem
{
    static char const* get_Default_File_Name() { return NULL; }

    Bool_Matrix* pm_;
    std::vector<Grid>* pgrids_;
    size_t m_, n_, grids_num_;

    void init(Bool_Matrix* pm, std::vector<Grid>* pg)
    {
        pm_        = pm;
        pgrids_    = pg;
        m_         = pm_->size();
        n_         = (m_ ? pm_->at(0).size() : 0);
        grids_num_ = pgrids_->size();
        //std::cout << "m_ = " << m_ << " n_ = " << n_ << " grids_num_ = " << grids_num_ << std::endl;
    }
};

class GridMin_Solution
{
    //DISALLOW_COPY_AND_ASSIGN(GridMin_Solution);
public:
    typedef int                     TCostType;
    typedef minimization<TCostType> TDirection;
    typedef Matrix2D<size_t>        IntMatrix;
    
    Subset    s_;

protected:
    TCostType cost_;
    bool      changed_;
    unsigned  hash_;
    bool      hash_changed_;
    
    IntMatrix hits_;

public:
    explicit GridMin_Solution():
        cost_(),
        changed_(),
        hash_(),
        hash_changed_()
    {}

    template<class E>
    void init(E* env, random_generator& rnd)
    {
        hits_.set_Resolution(env->problem_.m_, env->problem_.n_);
        hits_.fill(0);
    }

    template<class E>
    void inc_Hits(E* env, size_t i)
    {
        Grid& g = env->problem_.pgrids_->at(i);
        for (Subset::enumerator en_r = g.get_Rows().first(); en_r != g.get_Rows().end(); ++en_r)
        {
            for (Subset::enumerator en_c = g.get_Columns().first(); en_c != g.get_Columns().end(); ++en_c)
            {
                ++hits_[*en_r][*en_c];
            }
        }
    }

    template<class E>
    bool dec_Hits(E* env, size_t i)
    {
        bool flag = true;
        Grid& g = env->problem_.pgrids_->at(i);
        for (Subset::enumerator en_r = g.get_Rows().first(); en_r != g.get_Rows().end(); ++en_r)
        {
            for (Subset::enumerator en_c = g.get_Columns().first(); en_c != g.get_Columns().end(); ++en_c)
            {
                --hits_[*en_r][*en_c];
                if (hits_[*en_r][*en_c] == 0)
                {
                    flag = false;
                }
            }
        }

        return flag;
    }

    template<class E>
    void generate(E* env, random_generator& rnd)
    {
        for (size_t i = 0; i < env->problem_.grids_num_; ++i)
        {
            s_.insert(i);
            inc_Hits(env, i);
        }

        changed_ = true;
    }

    TCostType get_Cost() const
    {
        ASSERT_(!changed_);
        return cost_;
    }

    unsigned get_Hash()
    {
        ASSERT_(!changed_);
        if (hash_changed_)
        {
            hash_changed_ = false;
            hash_ = 0;//TODO fix me: for GA
        }
        return hash_;
    }

    template<class E>
    void evaluate(E* env, random_generator& rnd)
    {
        if (!changed_)
            return;
        changed_ = false;
        hash_changed_ = true;

        cost_ = s_.count();
    }

    template<class V>
    void accept(V& v)
    {
        v(cost_);
        v(changed_);
        v(hits_);
        v(hash_);
        v(hash_changed_);
        v(s_);
    }

    void assign(GridMin_Solution const& sol)
    {
        changed_        = sol.changed_;
        cost_           = sol.cost_;
        hash_           = sol.hash_;
        hash_changed_   = sol.hash_changed_;
        hits_           = sol.hits_;
        s_              = sol.s_;
    }

    template<class X>
    void copy_To(X& x) const
    {
        x.assign(*this);
    }

    template<class E>
    void print(std::ostream& os, E* env) const
    {
        PrintGridsSetTXT(os, *(env->problem_.pgrids_), s_);
    }

    template<class E>
    static bool read(E* env, std::string const& file_name, Solution_Pool<GridMin_Solution>& pool)
    {
        return false;
    }

    template<class E>
    void write(E* env, std::string const& file_name, size_t id, size_t count) const
    {
    }
};

template<>
struct solution<GridMin_Solution, sa_tag>:
    public GridMin_Solution
{
public:
    template<class E>
    void move(E* env, random_generator& rnd, double probability)
    {
        for (size_t i = 0; i <  env->problem_.grids_num_; ++i)
        {
            if (rnd.next_Double() < probability)
            {
                if (s_[i] == 0)   // add grid
                {
                    s_[i] = 1;
                    inc_Hits(env, i);
                    changed_ = true;
                }
                else              // remove grid
                {
                    bool ok = dec_Hits(env, i);
                    if (ok)
                    {
                        s_[i] = 0;
                        changed_ = true;
                    }
                    else
                    {
                        inc_Hits(env, i);
                    }
                }
            }
        }
    }
};

template<>
struct solution<GridMin_Solution, ga_tag>:
    public GridMin_Solution
{
public:
    mutable double wheel_sector_;

    // simple one-point crossover with validation
    template<class E>
    void cross(E* env, random_generator& rnd, solution<GridMin_Solution, ga_tag>& genome)
    {
        size_t j = rnd.next_SizeT(env->problem_.grids_num_);
        for (size_t i = j; i < env->problem_.grids_num_; ++i)
        {
            if (s_[i] == genome.s_[i])
            {
                //TODO fix me:
                //:? std::swap(s_[i], genome.s_[i]);
            }
            else
            {
                if (s_[i] == 1)
                {
                    genome.s_[i] = 1;
                    genome.inc_Hits(env, i);

                    bool ok = dec_Hits(env,i);
                    if (ok)
                    {
                        s_[i] = 0;
                    }
                    else
                    {
                        inc_Hits(env, i);
                    }
                }
                else
                {
                    s_[i] = 1;
                    inc_Hits(env, i);

                    bool ok = genome.dec_Hits(env,i);
                    if (ok)
                    {
                        genome.s_[i] = 0;
                    }
                    else
                    {
                        genome.inc_Hits(env, i);
                    }
                }
            }
        }
        changed_ = genome.changed_ = true;
    }

    // mutation a la move in SA
    template<class E>
    void mutate(E* env, random_generator& rnd, double mutationProbability)
    {
        for (size_t i = 0; i < env->problem_.grids_num_; ++i)
            if (rnd.next_Double() < mutationProbability)
            {
                if (s_[i] == 0)   // add grid
                {
                    s_[i] = 1;
                    inc_Hits(env, i);
                    changed_ = true;
                }
                else                      // remove grid
                {
                    bool ok = dec_Hits(env, i);
                    if (ok)
                    {
                        s_[i] = 0;
                        changed_ = true;
                    }
                    else
                    {
                        inc_Hits(env, i);
                    }
                }
            }
    }

    TCostType get_Fitness() const { return get_Cost(); }
};

//EOF!
